<!DOCTYPE HTML>
<!--
	Dimension by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
 <head>
  <title>
   Dimension by HTML5 UP
  </title>
  <!-- <meta charset="utf-8" /> -->
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> -->
  <meta charset="utf-8"/>
  <meta content="width=device-width,initial-scale=1.0" name="viewport"/>
  <link href="../../assets/css/article.css" rel="stylesheet"/>
  <link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet"/>
  <noscript>
   <link href="../../assets/css/noscript.css" rel="stylesheet"/>
  </noscript>
 </head>
 <body>
  <div id="app">
  </div>
  <!-- built files will be auto injected -->
 </body>
 <body class="is-preload">
  <!-- Wrapper -->
  <div id="wrapper">
   <!-- Main -->
   <div id="main">
    <article id="article">
     <h1 id="mybatis32">
      MyBatis3源码解析(2)数据库连接
     </h1>
     <hr/>
     <p>
      这是我参与2022首次更文挑战的第7天，活动详情查看：
      <a href="https://juejin.cn/post/7052884569032392740">
       2022首次更文挑战
      </a>
     </p>
     <h2 id="_1">
      简介
     </h2>
     <p>
      基于上篇的示例感受，下面我们探索下MyBatis连接数据库的细节是如果实现的，在日常使用中是如何能和Druid数据库连接池等配合起来的
     </p>
     <h2 id="_2">
      源码解析
     </h2>
     <p>
      基于上篇的示例代码：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MybatisTest</span> <span class="p">{</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">test</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">try</span><span class="p">(</span><span class="n">SqlSession</span> <span class="n">session</span> <span class="o">=</span> <span class="n">buildSqlSessionFactory</span><span class="p">().</span><span class="na">openSession</span><span class="p">())</span> <span class="p">{</span>
            <span class="n">PersonMapper</span> <span class="n">personMapper</span> <span class="o">=</span> <span class="n">session</span><span class="p">.</span><span class="na">getMapper</span><span class="p">(</span><span class="n">PersonMapper</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
            <span class="n">personMapper</span><span class="p">.</span><span class="na">createTable</span><span class="p">();</span>
            <span class="n">personMapper</span><span class="p">.</span><span class="na">save</span><span class="p">(</span><span class="n">Person</span><span class="p">.</span><span class="na">builder</span><span class="p">().</span><span class="na">id</span><span class="p">(</span><span class="mi">1L</span><span class="p">).</span><span class="na">name</span><span class="p">(</span><span class="s">"1"</span><span class="p">).</span><span class="na">build</span><span class="p">());</span>
            <span class="n">Person</span> <span class="n">person</span> <span class="o">=</span> <span class="n">personMapper</span><span class="p">.</span><span class="na">getPersonById</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
            <span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">person</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="n">SqlSessionFactory</span> <span class="nf">buildSqlSessionFactory</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">String</span> <span class="n">JDBC_DRIVER</span> <span class="o">=</span> <span class="s">"org.h2.Driver"</span><span class="p">;</span>
        <span class="n">String</span> <span class="n">DB_URL</span> <span class="o">=</span> <span class="s">"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"</span><span class="p">;</span>
        <span class="n">String</span> <span class="n">USER</span> <span class="o">=</span> <span class="s">"sa"</span><span class="p">;</span>
        <span class="n">String</span> <span class="n">PASS</span> <span class="o">=</span> <span class="s">""</span><span class="p">;</span>
        <span class="n">DataSource</span> <span class="n">dataSource</span> <span class="o">=</span> <span class="k">new</span> <span class="n">PooledDataSource</span><span class="p">(</span><span class="n">JDBC_DRIVER</span><span class="p">,</span> <span class="n">DB_URL</span><span class="p">,</span> <span class="n">USER</span><span class="p">,</span> <span class="n">PASS</span><span class="p">);</span>
        <span class="n">Environment</span> <span class="n">environment</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Environment</span><span class="p">(</span><span class="s">"Development"</span><span class="p">,</span> <span class="k">new</span> <span class="n">JdbcTransactionFactory</span><span class="p">(),</span> <span class="n">dataSource</span><span class="p">);</span>
        <span class="n">Configuration</span> <span class="n">configuration</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Configuration</span><span class="p">(</span><span class="n">environment</span><span class="p">);</span>
        <span class="n">configuration</span><span class="p">.</span><span class="na">addMapper</span><span class="p">(</span><span class="n">PersonMapper</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
        <span class="n">SqlSessionFactoryBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SqlSessionFactoryBuilder</span><span class="p">();</span>
        <span class="k">return</span> <span class="n">builder</span><span class="p">.</span><span class="na">build</span><span class="p">(</span><span class="n">configuration</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      当前我们想找的是与数据库建立连接的部分
     </p>
     <p>
      通过阅读书籍：《MyBatis3源码深度解析》，我们大概知道执行是在Executor中，我们跟踪相关的代码
     </p>
     <p>
      经过不懈的努力跟踪，得到下面的关键代码：
     </p>
     <p>
      找到了在执行语句中，在Executor中获取连接的关键代码
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SimpleExecutor</span> <span class="kd">extends</span> <span class="n">BaseExecutor</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="n">Statement</span> <span class="nf">prepareStatement</span><span class="p">(</span><span class="n">StatementHandler</span> <span class="n">handler</span><span class="p">,</span> <span class="n">Log</span> <span class="n">statementLog</span><span class="p">)</span> <span class="kd">throws</span> <span class="n">SQLException</span> <span class="p">{</span>
    <span class="c1">// 获取数据库连接</span>
        <span class="n">Connection</span> <span class="n">connection</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="na">getConnection</span><span class="p">(</span><span class="n">statementLog</span><span class="p">);</span>
        <span class="n">Statement</span> <span class="n">stmt</span> <span class="o">=</span> <span class="n">handler</span><span class="p">.</span><span class="na">prepare</span><span class="p">(</span><span class="n">connection</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="na">transaction</span><span class="p">.</span><span class="na">getTimeout</span><span class="p">());</span>
        <span class="n">handler</span><span class="p">.</span><span class="na">parameterize</span><span class="p">(</span><span class="n">stmt</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">stmt</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      继续跟踪，来到我们示例中定义的事务管理器：JdbcTransactionFactory
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">JdbcTransaction</span> <span class="kd">implements</span> <span class="n">Transaction</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="n">Connection</span> <span class="nf">getConnection</span><span class="p">()</span> <span class="kd">throws</span> <span class="n">SQLException</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">connection</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="na">openConnection</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="na">connection</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">openConnection</span><span class="p">()</span> <span class="kd">throws</span> <span class="n">SQLException</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">log</span><span class="p">.</span><span class="na">isDebugEnabled</span><span class="p">())</span> <span class="p">{</span>
            <span class="n">log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">"Opening JDBC Connection"</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="c1">// 从DataSource中获取</span>
        <span class="k">this</span><span class="p">.</span><span class="na">connection</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="na">dataSource</span><span class="p">.</span><span class="na">getConnection</span><span class="p">();</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">level</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="na">connection</span><span class="p">.</span><span class="na">setTransactionIsolation</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">level</span><span class="p">.</span><span class="na">getLevel</span><span class="p">());</span>
        <span class="p">}</span>

        <span class="k">this</span><span class="p">.</span><span class="na">setDesiredAutoCommit</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">autoCommit</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      上面可以看到是从DataSource中获取的，来到我们定义的：PooledDataSource
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PooledDataSource</span> <span class="kd">implements</span> <span class="n">DataSource</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="n">Connection</span> <span class="nf">getConnection</span><span class="p">()</span> <span class="kd">throws</span> <span class="n">SQLException</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="na">popConnection</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">dataSource</span><span class="p">.</span><span class="na">getUsername</span><span class="p">(),</span> <span class="k">this</span><span class="p">.</span><span class="na">dataSource</span><span class="p">.</span><span class="na">getPassword</span><span class="p">()).</span><span class="na">getProxyConnection</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="n">PooledConnection</span> <span class="nf">popConnection</span><span class="p">(</span><span class="n">String</span> <span class="n">username</span><span class="p">,</span> <span class="n">String</span> <span class="n">password</span><span class="p">)</span> <span class="kd">throws</span> <span class="n">SQLException</span> <span class="p">{</span>
        <span class="k">while</span><span class="p">(</span><span class="n">conn</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">synchronized</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">state</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">PoolState</span> <span class="n">var10000</span><span class="p">;</span>
                <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="na">state</span><span class="p">.</span><span class="na">idleConnections</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">())</span> <span class="p">{</span>
            <span class="p">......</span>
                <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">state</span><span class="p">.</span><span class="na">activeConnections</span><span class="p">.</span><span class="na">size</span><span class="p">()</span> <span class="o">&lt;</span> <span class="k">this</span><span class="p">.</span><span class="na">poolMaximumActiveConnections</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// 获取数据库连接池连接</span>
                    <span class="n">conn</span> <span class="o">=</span> <span class="k">new</span> <span class="n">PooledConnection</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">dataSource</span><span class="p">.</span><span class="na">getConnection</span><span class="p">(),</span> <span class="k">this</span><span class="p">);</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">log</span><span class="p">.</span><span class="na">isDebugEnabled</span><span class="p">())</span> <span class="p">{</span>
                        <span class="n">log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">"Created connection "</span> <span class="o">+</span> <span class="n">conn</span><span class="p">.</span><span class="na">getRealHashCode</span><span class="p">()</span> <span class="o">+</span> <span class="s">"."</span><span class="p">);</span>
                    <span class="p">}</span>
                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="p">......</span>
                <span class="p">}</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">conn</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="p">......</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">......</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      我们继续跟下去：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UnpooledDataSource</span> <span class="kd">implements</span> <span class="n">DataSource</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="n">Connection</span> <span class="nf">getConnection</span><span class="p">()</span> <span class="kd">throws</span> <span class="n">SQLException</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="na">doGetConnection</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">username</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="na">password</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="n">Connection</span> <span class="nf">doGetConnection</span><span class="p">(</span><span class="n">String</span> <span class="n">username</span><span class="p">,</span> <span class="n">String</span> <span class="n">password</span><span class="p">)</span> <span class="kd">throws</span> <span class="n">SQLException</span> <span class="p">{</span>
        <span class="n">Properties</span> <span class="n">props</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Properties</span><span class="p">();</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">driverProperties</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">props</span><span class="p">.</span><span class="na">putAll</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">driverProperties</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">username</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">props</span><span class="p">.</span><span class="na">setProperty</span><span class="p">(</span><span class="s">"user"</span><span class="p">,</span> <span class="n">username</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">password</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">props</span><span class="p">.</span><span class="na">setProperty</span><span class="p">(</span><span class="s">"password"</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="na">doGetConnection</span><span class="p">(</span><span class="n">props</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="n">Connection</span> <span class="nf">doGetConnection</span><span class="p">(</span><span class="n">Properties</span> <span class="n">properties</span><span class="p">)</span> <span class="kd">throws</span> <span class="n">SQLException</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="na">initializeDriver</span><span class="p">();</span>
    <span class="c1">// 看到了熟悉的原生的获取数据库连接的方式</span>
        <span class="n">Connection</span> <span class="n">connection</span> <span class="o">=</span> <span class="n">DriverManager</span><span class="p">.</span><span class="na">getConnection</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">url</span><span class="p">,</span> <span class="n">properties</span><span class="p">);</span>
        <span class="k">this</span><span class="p">.</span><span class="na">configureConnection</span><span class="p">(</span><span class="n">connection</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">connection</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      到这里我们找到了源码中如何获取数据库连接的关键代码
     </p>
     <p>
      其实就是对于原生数据库操作的封装
     </p>
     <h2 id="_3">
      调用栈回顾
     </h2>
     <p>
      我们回过头来看看整个过程中的类调用栈：
     </p>
     <ul>
      <li>
       MybatisTest：我们的测试代码
      </li>
      <li>
       MapperProxy：MyBatis的Mapper的代理类
      </li>
      <li>
       MapperMethod：SQL执行相关
      </li>
      <li>
       DefaultSqlSession
      </li>
      <li>
       CachingExecutor
      </li>
      <li>
       BaseExecutor
      </li>
      <li>
       SimpleExecutor
      </li>
      <li>
       JdbcTransation
      </li>
      <li>
       PooledDataSource
      </li>
      <li>
       UnpooledDataSource
      </li>
     </ul>
     <p>
      可以看到数据库连接是从DataSource中获取的，而PooledDataSource是MyBatis自己的一个数据库连接池，其中使用非池化的UnpooledDataSource
     </p>
     <p>
      在我们的日常开发中，经常使用MyBatis+Druid等数据库连接池一起使用，从这里就可以看到，我们简单替换一下MyBatis初始化时候的DataSource即可
     </p>
     <h2 id="_4">
      总结
     </h2>
     <p>
      本篇中，我们跟踪了数据库如何进行连接获取，看到日常开发中MyBatis+Druid等数据库连接的配置使用的背后原理
     </p>
    </article>
   </div>
   <!-- Footer -->
   <footer id="footer">
    <p class="copyright">
     © Untitled. Design:
     <a href="https://html5up.net">
      HTML5 UP
     </a>
     .
    </p>
   </footer>
  </div>
  <!-- BG -->
  <div id="bg">
  </div>
  <!-- Scripts -->
  <script src="../assets/js/jquery.min.js">
  </script>
  <script src="../assets/js/browser.min.js">
  </script>
  <script src="../assets/js/breakpoints.min.js">
  </script>
  <script src="../assets/js/util.js">
  </script>
  <script src="../assets/js/main.js">
  </script>
 </body>
</html>
